home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Internet Tools 1993 July / Internet Tools.iso / RockRidge / mail / mmdf / mmdf-IIb.43 / uip / ucbmail / list.c < prev    next >
Encoding:
C/C++ Source or Header  |  1986-02-01  |  12.3 KB  |  681 lines

  1. /*
  2.  * Copyright (c) 1980 Regents of the University of California.
  3.  * All rights reserved.  The Berkeley software License Agreement
  4.  * specifies the terms and conditions for redistribution.
  5.  */
  6.  
  7. #ifndef lint
  8. static char *sccsid = "@(#)list.c    5.4 (Berkeley) 11/2/85";
  9. #endif not lint
  10.  
  11. #include "./rcv.h"
  12. #include <ctype.h>
  13.  
  14. /*
  15.  * Mail -- a mail program
  16.  *
  17.  * Message list handling.
  18.  */
  19.  
  20. /*
  21.  * Convert the user string of message numbers and
  22.  * store the numbers into vector.
  23.  *
  24.  * Returns the count of messages picked up or -1 on error.
  25.  */
  26.  
  27. getmsglist(buf, vector, flags)
  28.     char *buf;
  29.     int *vector;
  30. {
  31.     register int *ip;
  32.     register struct message *mp;
  33.  
  34.     if (markall(buf, flags) < 0)
  35.         return(-1);
  36.     ip = vector;
  37.     for (mp = &message[0]; mp < &message[msgCount]; mp++)
  38.         if (mp->m_flag & MMARK)
  39.             *ip++ = mp - &message[0] + 1;
  40.     *ip = NULL;
  41.     return(ip - vector);
  42. }
  43.  
  44. /*
  45.  * Mark all messages that the user wanted from the command
  46.  * line in the message structure.  Return 0 on success, -1
  47.  * on error.
  48.  */
  49.  
  50. /*
  51.  * Bit values for colon modifiers.
  52.  */
  53.  
  54. #define    CMNEW        01        /* New messages */
  55. #define    CMOLD        02        /* Old messages */
  56. #define    CMUNREAD    04        /* Unread messages */
  57. #define    CMDELETED    010        /* Deleted messages */
  58. #define    CMREAD        020        /* Read messages */
  59.  
  60. /*
  61.  * The following table describes the letters which can follow
  62.  * the colon and gives the corresponding modifier bit.
  63.  */
  64.  
  65. struct coltab {
  66.     char    co_char;        /* What to find past : */
  67.     int    co_bit;            /* Associated modifier bit */
  68.     int    co_mask;        /* m_status bits to mask */
  69.     int    co_equal;        /* ... must equal this */
  70. } coltab[] = {
  71.     'n',        CMNEW,        MNEW,        MNEW,
  72.     'o',        CMOLD,        MNEW,        0,
  73.     'u',        CMUNREAD,    MREAD,        0,
  74.     'd',        CMDELETED,    MDELETED,    MDELETED,
  75.     'r',        CMREAD,        MREAD,        MREAD,
  76.     0,        0,        0,        0
  77. };
  78.  
  79. static    int    lastcolmod;
  80.  
  81. markall(buf, f)
  82.     char buf[];
  83. {
  84.     register char **np;
  85.     register int i;
  86.     register struct message *mp;
  87.     char *namelist[NMLSIZE], *bufp;
  88.     int tok, beg, mc, star, other, valdot, colmod, colresult;
  89.  
  90.     valdot = dot - &message[0] + 1;
  91.     colmod = 0;
  92.     for (i = 1; i <= msgCount; i++)
  93.         unmark(i);
  94.     bufp = buf;
  95.     mc = 0;
  96.     np = &namelist[0];
  97.     scaninit();
  98.     tok = scan(&bufp);
  99.     star = 0;
  100.     other = 0;
  101.     beg = 0;
  102.     while (tok != TEOL) {
  103.         switch (tok) {
  104.         case TNUMBER:
  105. number:
  106.             if (star) {
  107.                 printf("No numbers mixed with *\n");
  108.                 return(-1);
  109.             }
  110.             mc++;
  111.             other++;
  112.             if (beg != 0) {
  113.                 if (check(lexnumber, f))
  114.                     return(-1);
  115.                 for (i = beg; i <= lexnumber; i++)
  116.                     if ((message[i - 1].m_flag & MDELETED) 
  117. == f)
  118.                         mark(i);
  119.                 beg = 0;
  120.                 break;
  121.             }
  122.             beg = lexnumber;
  123.             if (check(beg, f))
  124.                 return(-1);
  125.             tok = scan(&bufp);
  126.             regret(tok);
  127.             if (tok != TDASH) {
  128.                 mark(beg);
  129.                 beg = 0;
  130.             }
  131.             break;
  132.  
  133.         case TPLUS:
  134.             if (beg != 0) {
  135.                 printf("Non-numeric second argument\n");
  136.                 return(-1);
  137.             }
  138.             if (valdot < msgCount)
  139.                 mark(valdot+1);
  140.             else {
  141.                     printf("Referencing beyond EOF\n");
  142.                     return(-1);
  143.                 }
  144.             break;
  145.  
  146.         case TDASH:
  147.             if (beg == 0) {
  148.                 if (valdot > 1)
  149.                     mark(valdot-1);
  150.                 else {
  151.                         printf("Referencing before 1\n");
  152.                         return(-1);
  153.                     }
  154.             }
  155.             break;
  156.  
  157.         case TSTRING:
  158.             if (beg != 0) {
  159.                 printf("Non-numeric second argument\n");
  160.                 return(-1);
  161.             }
  162.             other++;
  163.             if (lexstring[0] == ':') {
  164.                 colresult = evalcol(lexstring[1]);
  165.                 if (colresult == 0) {
  166.                     printf("Unknown colon modifier \"%s\"\n",
  167.                         lexstring);
  168.                     return(-1);
  169.                 }
  170.                 colmod |= colresult;
  171.             }
  172.             else
  173.                 *np++ = savestr(lexstring);
  174.             break;
  175.  
  176.         case TDOLLAR:
  177.         case TUP:
  178.         case TDOT:
  179.             lexnumber = metamess(lexstring[0], f);
  180.             if (lexnumber == -1)
  181.                 return(-1);
  182.             goto number;
  183.  
  184.         case TSTAR:
  185.             if (other) {
  186.                 printf("Can't mix \"*\" with anything\n");
  187.                 return(-1);
  188.             }
  189.             star++;
  190.             break;
  191.         }
  192.         tok = scan(&bufp);
  193.     }
  194.     lastcolmod = colmod;
  195.     *np = NOSTR;
  196.     mc = 0;
  197.     if (star) {
  198.         for (i = 0; i < msgCount; i++)
  199.             if ((message[i].m_flag & MDELETED) == f) {
  200.                 mark(i+1);
  201.                 mc++;
  202.             }
  203.         if (mc == 0) {
  204.             printf("No applicable messages.\n");
  205.             return(-1);
  206.         }
  207.         return(0);
  208.     }
  209.  
  210.     /*
  211.      * If no numbers were given, mark all of the messages,
  212.      * so that we can unmark any whose sender was not selected
  213.      * if any user names were given.
  214.      */
  215.  
  216.     if ((np > namelist || colmod != 0) && mc == 0)
  217.         for (i = 1; i <= msgCount; i++)
  218.             if ((message[i-1].m_flag & MDELETED) == f)
  219.                 mark(i);
  220.  
  221.     /*
  222.      * If any names were given, go through and eliminate any
  223.      * messages whose senders were not requested.
  224.      */
  225.  
  226.     if (np > namelist) {
  227.         for (i = 1; i <= msgCount; i++) {
  228.             for (mc = 0, np = &namelist[0]; *np != NOSTR; np++)
  229.                 if (**np == '/') {
  230.                     if (matchsubj(*np, i)) {
  231.                         mc++;
  232.                         break;
  233.                     }
  234.                 }
  235.                 else {
  236.                     if (sender(*np, i)) {
  237.                         mc++;
  238.                         break;
  239.                     }
  240.                 }
  241.             if (mc == 0)
  242.                 unmark(i);
  243.         }
  244.  
  245.         /*
  246.          * Make sure we got some decent messages.
  247.          */
  248.  
  249.         mc = 0;
  250.         for (i = 1; i <= msgCount; i++)
  251.             if (message[i-1].m_flag & MMARK) {
  252.                 mc++;
  253.                 break;
  254.             }
  255.         if (mc == 0) {
  256.             printf("No applicable messages from {%s",
  257.                 namelist[0]);
  258.             for (np = &namelist[1]; *np != NOSTR; np++)
  259.                 printf(", %s", *np);
  260.             printf("}\n");
  261.             return(-1);
  262.         }
  263.     }
  264.  
  265.     /*
  266.      * If any colon modifiers were given, go through and
  267.      * unmark any messages which do not satisfy the modifiers.
  268.      */
  269.  
  270.     if (colmod != 0) {
  271.         for (i = 1; i <= msgCount; i++) {
  272.             register struct coltab *colp;
  273.  
  274.             mp = &message[i - 1];
  275.             for (colp = &coltab[0]; colp->co_char; colp++)
  276.                 if (colp->co_bit & colmod)
  277.                     if ((mp->m_flag & colp->co_mask)
  278.                         != colp->co_equal)
  279.                         unmark(i);
  280.             
  281.         }
  282.         for (mp = &message[0]; mp < &message[msgCount]; mp++)
  283.             if (mp->m_flag & MMARK)
  284.                 break;
  285.         if (mp >= &message[msgCount]) {
  286.             register struct coltab *colp;
  287.  
  288.             printf("No messages satisfy");
  289.             for (colp = &coltab[0]; colp->co_char; colp++)
  290.                 if (colp->co_bit & colmod)
  291.                     printf(" :%c", colp->co_char);
  292.             printf("\n");
  293.             return(-1);
  294.         }
  295.     }
  296.     return(0);
  297. }
  298.  
  299. /*
  300.  * Turn the character after a colon modifier into a bit
  301.  * value.
  302.  */
  303. evalcol(col)
  304. {
  305.     register struct coltab *colp;
  306.  
  307.     if (col == 0)
  308.         return(lastcolmod);
  309.     for (colp = &coltab[0]; colp->co_char; colp++)
  310.         if (colp->co_char == col)
  311.             return(colp->co_bit);
  312.     return(0);
  313. }
  314.  
  315. /*
  316.  * Check the passed message number for legality and proper flags.
  317.  */
  318.  
  319. check(mesg, f)
  320. {
  321.     register struct message *mp;
  322.  
  323.     if (mesg < 1 || mesg > msgCount) {
  324.         printf("%d: Invalid message number\n", mesg);
  325.         return(-1);
  326.     }
  327.     mp = &message[mesg-1];
  328.     if ((mp->m_flag & MDELETED) != f) {
  329.         printf("%d: Inappropriate message\n", mesg);
  330.         return(-1);
  331.     }
  332.     return(0);
  333. }
  334.  
  335. /*
  336.  * Scan out the list of string arguments, shell style
  337.  * for a RAWLIST.
  338.  */
  339.  
  340. getrawlist(line, argv, argc)
  341.     char line[];
  342.     char **argv;
  343.     int  argc;
  344. {
  345.     register char **ap, *cp, *cp2;
  346.     char linebuf[BUFSIZ], quotec;
  347.     register char **last;
  348.  
  349.     ap = argv;
  350.     cp = line;
  351.     last = argv + argc - 1;
  352.     while (*cp != '\0') {
  353.         while (any(*cp, " \t"))
  354.             cp++;
  355.         cp2 = linebuf;
  356.         quotec = 0;
  357.         if (any(*cp, "'\""))
  358.             quotec = *cp++;
  359.         if (quotec == 0)
  360.             while (*cp != '\0' && !any(*cp, " \t"))
  361.                 *cp2++ = *cp++;
  362.         else {
  363.             while (*cp != '\0' && *cp != quotec)
  364.                 *cp2++ = *cp++;
  365.             if (*cp != '\0')
  366.                 cp++;
  367.         }
  368.         *cp2 = '\0';
  369.         if (cp2 == linebuf)
  370.             break;
  371.         if (ap >= last) {
  372.             printf("Too many elements in the list; excess discarded\n");
  373.             break;
  374.         }
  375.         *ap++ = savestr(linebuf);
  376.     }
  377.     *ap = NOSTR;
  378.     return(ap-argv);
  379. }
  380.  
  381. /*
  382.  * scan out a single lexical item and return its token number,
  383.  * updating the string pointer passed **p.  Also, store the value
  384.  * of the number or string scanned in lexnumber or lexstring as
  385.  * appropriate.  In any event, store the scanned `thing' in lexstring.
  386.  */
  387.  
  388. struct lex {
  389.     char    l_char;
  390.     char    l_token;
  391. } singles[] = {
  392.     '$',    TDOLLAR,
  393.     '.',    TDOT,
  394.     '^',    TUP,
  395.     '*',    TSTAR,
  396.     '-',    TDASH,
  397.     '+',    TPLUS,
  398.     '(',    TOPEN,
  399.     ')',    TCLOSE,
  400.     0,    0
  401. };
  402.  
  403. scan(sp)
  404.     char **sp;
  405. {
  406.     register char *cp, *cp2;
  407.     register int c;
  408.     register struct lex *lp;
  409.     int quotec;
  410.  
  411.     if (regretp >= 0) {
  412.         copy(stringstack[regretp], lexstring);
  413.         lexnumber = numberstack[regretp];
  414.         return(regretstack[regretp--]);
  415.     }
  416.     cp = *sp;
  417.     cp2 = lexstring;
  418.     c = *cp++;
  419.  
  420.     /*
  421.      * strip away leading white space.
  422.      */
  423.  
  424.     while (any(c, " \t"))
  425.         c = *cp++;
  426.  
  427.     /*
  428.      * If no characters remain, we are at end of line,
  429.      * so report that.
  430.      */
  431.  
  432.     if (c == '\0') {
  433.         *sp = --cp;
  434.         return(TEOL);
  435.     }
  436.  
  437.     /*
  438.      * If the leading character is a digit, scan
  439.      * the number and convert it on the fly.
  440.      * Return TNUMBER when done.
  441.      */
  442.  
  443.     if (isdigit(c)) {
  444.         lexnumber = 0;
  445.         while (isdigit(c)) {
  446.             lexnumber = lexnumber*10 + c - '0';
  447.             *cp2++ = c;
  448.             c = *cp++;
  449.         }
  450.         *cp2 = '\0';
  451.         *sp = --cp;
  452.         return(TNUMBER);
  453.     }
  454.  
  455.     /*
  456.      * Check for single character tokens; return such
  457.      * if found.
  458.      */
  459.  
  460.     for (lp = &singles[0]; lp->l_char != 0; lp++)
  461.         if (c == lp->l_char) {
  462.             lexstring[0] = c;
  463.             lexstring[1] = '\0';
  464.             *sp = cp;
  465.             return(lp->l_token);
  466.         }
  467.  
  468.     /*
  469.      * We've got a string!  Copy all the characters
  470.      * of the string into lexstring, until we see
  471.      * a null, space, or tab.
  472.      * If the lead character is a " or ', save it
  473.      * and scan until you get another.
  474.      */
  475.  
  476.     quotec = 0;
  477.     if (any(c, "'\"")) {
  478.         quotec = c;
  479.         c = *cp++;
  480.     }
  481.     while (c != '\0') {
  482.         if (c == quotec)
  483.             break;
  484.         if (quotec == 0 && any(c, " \t"))
  485.             break;
  486.         if (cp2 - lexstring < STRINGLEN-1)
  487.             *cp2++ = c;
  488.         c = *cp++;
  489.     }
  490.     if (quotec && c == 0)
  491.         fprintf(stderr, "Missing %c\n", quotec);
  492.     *sp = --cp;
  493.     *cp2 = '\0';
  494.     return(TSTRING);
  495. }
  496.  
  497. /*
  498.  * Unscan the named token by pushing it onto the regret stack.
  499.  */
  500.  
  501. regret(token)
  502. {
  503.     if (++regretp >= REGDEP)
  504.         panic("Too many regrets");
  505.     regretstack[regretp] = token;
  506.     lexstring[STRINGLEN-1] = '\0';
  507.     stringstack[regretp] = savestr(lexstring);
  508.     numberstack[regretp] = lexnumber;
  509. }
  510.  
  511. /*
  512.  * Reset all the scanner global variables.
  513.  */
  514.  
  515. scaninit()
  516. {
  517.     regretp = -1;
  518. }
  519.  
  520. /*
  521.  * Find the first message whose flags & m == f  and return
  522.  * its message number.
  523.  */
  524.  
  525. first(f, m)
  526. {
  527.     register int mesg;
  528.     register struct message *mp;
  529.  
  530.     mesg = dot - &message[0] + 1;
  531.     f &= MDELETED;
  532.     m &= MDELETED;
  533.     for (mp = dot; mp < &message[msgCount]; mp++) {
  534.         if ((mp->m_flag & m) == f)
  535.             return(mesg);
  536.         mesg++;
  537.     }
  538.     mesg = dot - &message[0];
  539.     for (mp = dot-1; mp >= &message[0]; mp--) {
  540.         if ((mp->m_flag & m) == f)
  541.             return(mesg);
  542.         mesg--;
  543.     }
  544.     return(NULL);
  545. }
  546.  
  547. /*
  548.  * See if the passed name sent the passed message number.  Return true
  549.  * if so.
  550.  */
  551.  
  552. sender(str, mesg)
  553.     char *str;
  554. {
  555.     register struct message *mp;
  556.     register char *cp;
  557.  
  558.     mp = &message[mesg-1];
  559.     cp = nameof(mp, 0);
  560.     return(icequal(cp, str));
  561. }
  562.  
  563. /*
  564.  * See if the given string matches inside the subject field of the
  565.  * given message.  For the purpose of the scan, we ignore case differences.
  566.  * If it does, return true.  The string search argument is assumed to
  567.  * have the form "/search-string."  If it is of the form "/," we use the
  568.  * previous search string.
  569.  */
  570.  
  571. char lastscan[128];
  572.  
  573. matchsubj(str, mesg)
  574.     char *str;
  575. {
  576.     register struct message *mp;
  577.     register char *cp, *cp2, *backup;
  578.  
  579.     str++;
  580.     if (strlen(str) == 0)
  581.         str = lastscan;
  582.     else
  583.         strcpy(lastscan, str);
  584.     mp = &message[mesg-1];
  585.     
  586.     /*
  587.      * Now look, ignoring case, for the word in the string.
  588.      */
  589.  
  590.     cp = str;
  591.     cp2 = hfield("subject", mp);
  592.     if (cp2 == NOSTR)
  593.         return(0);
  594.     backup = cp2;
  595.     while (*cp2) {
  596.         if (*cp == 0)
  597.             return(1);
  598.         if (raise(*cp++) != raise(*cp2++)) {
  599.             cp2 = ++backup;
  600.             cp = str;
  601.         }
  602.     }
  603.     return(*cp == 0);
  604. }
  605.  
  606. /*
  607.  * Mark the named message by setting its mark bit.
  608.  */
  609.  
  610. mark(mesg)
  611. {
  612.     register int i;
  613.  
  614.     i = mesg;
  615.     if (i < 1 || i > msgCount)
  616.         panic("Bad message number to mark");
  617.     message[i-1].m_flag |= MMARK;
  618. }
  619.  
  620. /*
  621.  * Unmark the named message.
  622.  */
  623.  
  624. unmark(mesg)
  625. {
  626.     register int i;
  627.  
  628.     i = mesg;
  629.     if (i < 1 || i > msgCount)
  630.         panic("Bad message number to unmark");
  631.     message[i-1].m_flag &= ~MMARK;
  632. }
  633.  
  634. /*
  635.  * Return the message number corresponding to the passed meta character.
  636.  */
  637.  
  638. metamess(meta, f)
  639. {
  640.     register int c, m;
  641.     register struct message *mp;
  642.  
  643.     c = meta;
  644.     switch (c) {
  645.     case '^':
  646.         /*
  647.          * First 'good' message left.
  648.          */
  649.         for (mp = &message[0]; mp < &message[msgCount]; mp++)
  650.             if ((mp->m_flag & MDELETED) == f)
  651.                 return(mp - &message[0] + 1);
  652.         printf("No applicable messages\n");
  653.         return(-1);
  654.  
  655.     case '$':
  656.         /*
  657.          * Last 'good message left.
  658.          */
  659.         for (mp = &message[msgCount-1]; mp >= &message[0]; mp--)
  660.             if ((mp->m_flag & MDELETED) == f)
  661.                 return(mp - &message[0] + 1);
  662.         printf("No applicable messages\n");
  663.         return(-1);
  664.  
  665.     case '.':
  666.         /* 
  667.          * Current message.
  668.          */
  669.         m = dot - &message[0] + 1;
  670.         if ((dot->m_flag & MDELETED) != f) {
  671.             printf("%d: Inappropriate message\n", m);
  672.             return(-1);
  673.         }
  674.         return(m);
  675.  
  676.     default:
  677.         printf("Unknown metachar (%c)\n", c);
  678.         return(-1);
  679.     }
  680. }
  681.